package star.oath.framework.util;

import java.math.*;
import java.util.*;
import java.util.stream.*;

public class MathematicsUtils {

    /**
     * 将单个对象转换为BigDecimal
     * @param value 输入对象
     * @return 转换后的BigDecimal，无法转换时返回null
     */
    public static BigDecimal convertToBigDecimal(Object value) {
        if (value == null) {
            return null;
        }
        try {
            if (value instanceof BigDecimal) {
                return (BigDecimal) value;
            }
            else if (value instanceof String) {
                String strValue = (String) value;
                if (strValue.trim().isEmpty()) {
                    return null;
                }
                return new BigDecimal(strValue);
            }
            else if (value instanceof Number) {
                return new BigDecimal(value.toString());
            }
            else {
                // 尝试使用toString()方法转换
                String strValue = value.toString();
                if (strValue.trim().isEmpty()) {
                    return null;
                }
                return new BigDecimal(strValue);
            }
        }
        catch (Exception e) {
            // 记录日志或处理转换异常
            System.err.println("无法将值转换为BigDecimal: " + value);
            return null;
        }
    }

    /**
     * 将List转换为BigDecimal数组
     * 支持的元素类型：String、Number及其子类（如Integer、Double等）
     * 空值或不可转换的值会被转换为null
     * @param list 输入列表
     * @return BigDecimal数组
     */
    public static BigDecimal[] toBigDecimalArray(List<?> list) {
        if (list == null) {
            return new BigDecimal[0];
        }
        return list.stream()
                .map(MathematicsUtils::convertToBigDecimal)
                .collect(Collectors.toList())
                .toArray(new BigDecimal[0]);
    }

    /**
     * 获取不定数量数字中的最大值
     * 支持 Integer, Long, Float, Double, BigDecimal 等数字类型
     * @param numbers 不定数量的数字
     * @return 最大值的 BigDecimal 表示形式，如果参数为空则返回 null
     */
    public static BigDecimal max(Number... numbers) {
        return maximum(numbers);
    }
    /**
     * 获取不定数量数字中的最大值
     * 支持 Integer, Long, Float, Double, BigDecimal 等数字类型
     * @param numbers 不定数量的数字
     * @return 最大值的 BigDecimal 表示形式，如果参数为空则返回 null
     */
    public static BigDecimal maximum(Number... numbers) {
        if (numbers == null || numbers.length == 0) {
            return null;
        }
        BigDecimal max = new BigDecimal(numbers[0].toString());
        for (int i = 1; i < numbers.length; i++) {
            BigDecimal current = new BigDecimal(numbers[i].toString());
            if (current.compareTo(max) > 0) {
                max = current;
            }
        }
        return max;
    }
    /**
     * 获取不定数量数字中的最大值
     * 支持 Integer, Long, Float, Double, BigDecimal 等数字类型
     * @param numberList 数字集合
     * @return 最大值的 BigDecimal 表示形式，如果参数为空则返回 null
     */
    public static <T> BigDecimal max(List<T> numberList) {
        return maximum(numberList);
    }
    /**
     * 获取不定数量数字中的最大值
     * 支持 Integer, Long, Float, Double, BigDecimal 等数字类型
     * @param numberList 数字集合
     * @return 最大值的 BigDecimal 表示形式，如果参数为空则返回 null
     */
    public static <T> BigDecimal maximum(List<T> numberList) {
        if (numberList == null || numberList.size() == 0) {
            return null;
        }
        BigDecimal max = new BigDecimal(numberList.get(0).toString());
        for (int i = 1; i < numberList.size(); i++) {
            BigDecimal current = new BigDecimal(numberList.get(i).toString());
            if (current.compareTo(max) > 0) {
                max = current;
            }
        }
        return max;
    }

    /**
     * 获取不定数量数字中的最小值
     * 支持 Integer, Long, Float, Double, BigDecimal 等数字类型
     * @param numbers 不定数量的数字
     * @return 最小值的 BigDecimal 表示形式，如果参数为空则返回 null
     */
    public static BigDecimal min(Number... numbers) {
        return minimum(numbers);
    }
    /**
     * 获取不定数量数字中的最小值
     * 支持 Integer, Long, Float, Double, BigDecimal 等数字类型
     * @param numbers 不定数量的数字
     * @return 最小值的 BigDecimal 表示形式，如果参数为空则返回 null
     */
    public static BigDecimal minimum(Number... numbers) {
        if (numbers == null || numbers.length == 0) {
            return null;
        }
        BigDecimal min = new BigDecimal(numbers[0].toString());
        for (int i = 1; i < numbers.length; i++) {
            BigDecimal current = new BigDecimal(numbers[i].toString());
            if (current.compareTo(min) < 0) {
                min = current;
            }
        }
        return min;
    }

    /**
     * 计算不定数量数字的总和
     * @param numbers 不定数量的数字
     * @return 总和的 BigDecimal 表示形式，如果参数为空则返回 null
     */
    public static BigDecimal sum(Number... numbers) {
        if (numbers == null || numbers.length == 0) {
            return null;
        }
        BigDecimal total = BigDecimal.ZERO;
        for (Number num : numbers) {
            total = total.add(new BigDecimal(num.toString()));
        }
        return total;
    }

    /**
     * 计算不定数量数字的平均值
     * @param numbers 不定数量的数字
     * @param scale 精度位数
     * @return 平均值的 BigDecimal 表示形式，如果参数为空则返回 null
     */
    public static BigDecimal average(int scale, Number... numbers) {
        if (numbers == null || numbers.length == 0) {
            return null;
        }
        BigDecimal total = sum(numbers);
        return total.divide(BigDecimal.valueOf(numbers.length), scale, RoundingMode.HALF_UP);
    }

    /**
     * 计算中位数
     * @param numbers 数字数组
     * @return 中位数的BigDecimal表示形式，如果参数为空则返回null
     */
    public static BigDecimal median(Number... numbers) {
        if (numbers == null || numbers.length == 0) {
            return null;
        }
        // 转换为BigDecimal数组并排序
        BigDecimal[] decimalNumbers = Arrays.stream(numbers)
                .map(n -> new BigDecimal(n.toString()))
                .sorted()
                .toArray(BigDecimal[]::new);
        int length = decimalNumbers.length;
        if (length % 2 == 0) {
            // 偶数个元素，取中间两个的平均值
            BigDecimal mid1 = decimalNumbers[length / 2 - 1];
            BigDecimal mid2 = decimalNumbers[length / 2];
            return mid1.add(mid2).divide(BigDecimal.valueOf(2), 10, RoundingMode.HALF_UP);
        }
        else {
            // 奇数个元素，取中间值
            return decimalNumbers[length / 2];
        }
    }

    /**
     * 计算方差
     * @param numbers 数字数组
     * @return 方差的BigDecimal表示形式，如果参数为空则返回null
     */
    public static BigDecimal variance(Number... numbers) {
        if (numbers == null || numbers.length <= 1) {
            return null;
        }
        BigDecimal mean = average(10, numbers);
        BigDecimal sumSquaredDiff = BigDecimal.ZERO;

        for (Number num : numbers) {
            BigDecimal diff = new BigDecimal(num.toString()).subtract(mean);
            sumSquaredDiff = sumSquaredDiff.add(diff.pow(2));
        }
        return sumSquaredDiff.divide(BigDecimal.valueOf(numbers.length - 1), 10, RoundingMode.HALF_UP);
    }

    /**
     * 计算标准差
     * @param numbers 数字数组
     * @return 标准差的BigDecimal表示形式，如果参数为空则返回null
     */
    public static BigDecimal standardDeviation(Number... numbers) {
        BigDecimal variance = variance(numbers);
        if (variance == null) {
            return null;
        }
        // 使用sqrt方法计算平方根
        return sqrt(variance, 10);
    }

    /**
     * 计算一个数的指定幂次方
     * @param base 底数
     * @param exponent 指数
     * @return 幂次方的结果
     */
    public static BigDecimal power(Number base, int exponent) {
        BigDecimal baseBD = convertToBigDecimal(base);
        if (baseBD == null) {
            return null;
        }
        return baseBD.pow(exponent);
    }

    /**
     * 计算平方根（BigDecimal没有直接的sqrt方法）
     * @param value 要计算平方根的值
     * @param scale 精度位数
     * @return 平方根的BigDecimal表示形式
     */
    private static BigDecimal sqrt(BigDecimal value, int scale) {
        if (value.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("Cannot calculate square root of negative number");
        }
        // 使用牛顿迭代法计算平方根
        BigDecimal x = value;
        BigDecimal lastX = BigDecimal.ZERO;
        while (true) {
            lastX = x;
            x = value.divide(x, scale, RoundingMode.HALF_UP)
                    .add(x)
                    .divide(BigDecimal.valueOf(2), scale, RoundingMode.HALF_UP);

            if (x.subtract(lastX).abs().compareTo(BigDecimal.valueOf(1e-10)) <= 0) {
                break;
            }
        }
        return x.setScale(scale, RoundingMode.HALF_UP);
    }

    /**
     * 计算一个整数的阶乘
     * @param number 输入的整数
     * @return 阶乘的结果，如果输入为负数则返回 null
     */
    public static BigDecimal factorial(int number) {
        if (number < 0) {
            return null;
        }
        BigDecimal result = BigDecimal.ONE;
        for (int i = 2; i <= number; i++) {
            result = result.multiply(BigDecimal.valueOf(i));
        }
        return result;
    }

    /**
     * 计算组合数 C(n, k)
     * @param n 元素总数
     * @param k 选取的元素个数
     * @return 组合数的结果，如果输入不合法则返回 null
     */
    public static BigDecimal combination(int n, int k) {
        if (n < 0 || k < 0 || k > n) {
            return null;
        }
        BigDecimal numerator = factorial(n);
        BigDecimal denominator = factorial(k).multiply(factorial(n - k));
        return numerator.divide(denominator, 10, RoundingMode.HALF_UP);
    }

    /**
     * 计算两个整数的最大公约数
     * @param a 第一个整数
     * @param b 第二个整数
     * @return 最大公约数
     */
    public static int gcd(int a, int b) {
        return greatestCommonDivisor(a, b);
    }
    /**
     * 计算两个整数的最大公约数
     * @param a 第一个整数
     * @param b 第二个整数
     * @return 最大公约数
     */
    public static int greatestCommonDivisor(int a, int b) {
        while (b != 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }

    /**
     * 计算两个整数的最小公倍数
     * @param a 第一个整数
     * @param b 第二个整数
     * @return 最小公倍数
     */
    public static int lcm(int a, int b) {
        return leastCommonMultiple(a, b);
    }
    /**
     * 计算两个整数的最小公倍数
     * @param a 第一个整数
     * @param b 第二个整数
     * @return 最小公倍数
     */
    public static int leastCommonMultiple(int a, int b) {
        return a * b / gcd(a, b);
    }

    /**
     * 比较两个未知类型数字的大小
     * @param num1 第一个数字
     * @param num2 第二个数字
     * @return -1（num1 < num2）、0（num1 == num2）、1（num1 > num2）
     */
    public static int compare(Number num1, Number num2) {
        if (num1 == null && num2 == null) {
            return 0;
        }
        if (num1 == null) {
            return -1;
        }
        if (num2 == null) {
            return 1;
        }
        BigDecimal bd1 = new BigDecimal(num1.toString());
        BigDecimal bd2 = new BigDecimal(num2.toString());
        return bd1.compareTo(bd2);
    }

    /**
     * 判断第一个数是否大于第二个数
     * @param num1 第一个数字
     * @param num2 第二个数字
     * @return true（num1 > num2）或 false
     */
    public static boolean isGreaterThan(Number num1, Number num2) {
        return compare(num1, num2) > 0;
    }

    /**
     * 判断第一个数是否小于第二个数
     * @param num1 第一个数字
     * @param num2 第二个数字
     * @return true（num1 < num2）或 false
     */
    public static boolean isLessThan(Number num1, Number num2) {
        return compare(num1, num2) < 0;
    }

    /**
     * 判断两个数是否相等
     * @param num1 第一个数字
     * @param num2 第二个数字
     * @return true（num1 == num2）或 false
     */
    public static boolean isEqual(Number num1, Number num2) {
        return compare(num1, num2) == 0;
    }

    /**
     * 获取百分比
     * @param dividend 被除数
     * @param divisor 除数
     * @param point 保留小数位数 默认2位
     * @param round 进位方式
     * @return 百分比
     */
    public static BigDecimal percentage(Number dividend, Number divisor, int point, RoundingMode round) {
        BigDecimal dividendBD = new BigDecimal(String.valueOf(dividend));
        BigDecimal divisorBD = new BigDecimal(String.valueOf(divisor));
        BigDecimal zero = new BigDecimal(0);
        BigDecimal h = new BigDecimal(100);
        BigDecimal percentage;
        if (zero.compareTo(dividendBD)!=0&&zero.compareTo(divisorBD)!=0){
            percentage = dividendBD.multiply(h).divide(divisorBD,point,round);
        }
        else if (zero.compareTo(dividendBD)!=0&&zero.compareTo(divisorBD)==0){
            percentage = h;
        }
        else if (zero.compareTo(dividendBD)==0&&zero.compareTo(divisorBD)!=0){
            percentage = zero;
        }
        else {
            BigDecimal n = new BigDecimal(1);
            percentage = zero.divide(n,point,round);
        }
        return percentage;
    }

    /**
     * 获取两个数的变化率
     * @param thisNum 本次的数
     * @param lastNum 上次的数
     * @param point 保留小数位数 默认2位
     * @param round 进位方式
     * @return
     */
    public static BigDecimal changeRate(Number thisNum, Number lastNum, int point, RoundingMode round) {
        BigDecimal thisBD = new BigDecimal(String.valueOf(thisNum));
        BigDecimal lastBD = new BigDecimal(String.valueOf(lastNum));
        BigDecimal zero = new BigDecimal(0);
        BigDecimal h = new BigDecimal(100);
        BigDecimal changeRateBD;
        if (zero.compareTo(thisBD)!=0&&zero.compareTo(lastBD)!=0){
            BigDecimal changeNumBD = thisBD.subtract(lastBD);
            changeRateBD = changeNumBD.multiply(h).divide(lastBD,point,round);
        }
        else if (zero.compareTo(thisBD)!=0&&zero.compareTo(lastBD)==0){
            BigDecimal n = new BigDecimal(1);
            BigDecimal x = new BigDecimal(1);
            changeRateBD = x.multiply(h).divide(n,point,round);
        }
        else if (zero.compareTo(thisBD)==0&&zero.compareTo(lastBD)!=0){
            BigDecimal n = new BigDecimal(1);
            BigDecimal x = new BigDecimal(-1);
            changeRateBD = x.multiply(h).divide(n,point,round);
        }
        else {
            BigDecimal n = new BigDecimal(1);
            changeRateBD = zero.divide(n,point,round);
        }
        return changeRateBD;
    }

}
